package com.bbzn.device.client.controller;

import com.bbzn.device.client.vo.req.GroupDeviceVO;
import com.bbzn.device.client.vo.req.GroupLinkVO;
import com.bbzn.device.client.vo.req.GroupVO;
import com.bbzn.device.client.aspect.OperationAnnotation;
import com.bbzn.device.client.config.EmqxMqttProperties;
import com.bbzn.device.client.config.Oauth2Constant;
import com.bbzn.device.client.constant.CrudConstant;
import com.bbzn.device.client.constant.DeviceConstant;
import com.bbzn.device.client.constant.GroupConstant;
import com.bbzn.device.client.dao.mqttsub.EmqApiEntity;
import com.bbzn.device.client.dataobject.*;
import com.bbzn.device.client.dto.group.*;
import com.bbzn.device.client.exception.DefinitException;
import com.bbzn.device.client.service.*;
import com.bbzn.device.client.utils.*;
import com.bbzn.device.client.utils.date.DateUtil;
import io.swagger.annotations.Api;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author wulongbo
 * @Date 2021/7/1 14:19
 * @Version 1.0
 */

@Api(value = "Group", tags = "分组")
@RestController
@RequestMapping("/group")
public class GroupController extends BaseApiService {

    @Autowired
    private CommonService commonService;

    @Autowired
    private SystemParamByCacheService systemParamByCacheService;

    @Autowired
    private GroupService groupService;

    @Autowired
    private GroupDeviceService groupDeviceService;

    @Autowired
    private GroupLinkService groupLinkService;

    @Autowired
    private NbPublishService nbPublishService;

    @Autowired
    private MqttSubService mqttSubService;

    @Autowired
    private EmqApiService emqApiService;

    @Resource
    private EmqxMqttProperties emqxMqttProperties;

    //todo 测试时所有接口对照业务

    /**
     * 新增分组
     *
     * @param groupVO
     * @return
     */
    @RequestMapping(value = "/addGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "新增分组")
    public BaseResponse addGroup(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        String userName = MayiktJwtUtils.getUsername(request, secretKey);
        if (!OptionalBean.ofNullable(groupVO.getPid()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_PID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        if (!OptionalBean.ofNullable(groupVO.getGroupName()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_NAME_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        Group group = BabaBeanUtils.voToDto(groupVO, Group.class);
        List<Group> groups = groupService.findByAll(group);
        if (CollectionUtils.isNotEmpty(groups)) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NAME_EXIST_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        if (null != group.getState()) {
            group.setState(groupVO.getState());
        }
        if (null != group.getType()) {
            group.setType(groupVO.getType());
        }
        group.setUserId(userId);
        group.setCreateTime(new Date());
        group.setCreatedBy(userName);
        groupService.insertSelective(group);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.ADD_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }

    /**
     * 修改分组数据
     *
     * @param groupVO
     * @return
     */
    @RequestMapping(value = "/updateGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "修改分组数据")
    public BaseResponse updateGroup(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        String userName = MayiktJwtUtils.getUsername(request, secretKey);
        if (!OptionalBean.ofNullable(groupVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        if (!OptionalBean.ofNullable(groupVO.getGroupName()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_NAME_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }

        Group group = new Group();
        group.setGroupName(groupVO.getGroupName());
//        List<Group> groups = groupService.findByAll(group);
//        if (CollectionUtils.isNotEmpty(groups)) {
//            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NAME_EXIST_PREFIX + language);
//            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
//        }
        if (null != group.getState()) {
            group.setState(groupVO.getState());
        }
        if (null != group.getType()) {
            group.setType(groupVO.getType());
        }
        group.setUserId(userId);
        group.setId(groupVO.getId());
        group.setUpdateTime(new Date());
        group.setUpdatedBy(userName);
        groupService.updateByPrimaryKeySelective(group);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.MODIFY_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }


    /**
     * 修改分组pid
     *
     * @param groupVO
     * @return
     */
    @RequestMapping(value = "/updatePid", method = RequestMethod.POST)
    @OperationAnnotation(content = "修改分组pid")
    public BaseResponse updatePid(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        String userName = MayiktJwtUtils.getUsername(request, secretKey);
        if (!OptionalBean.ofNullable(groupVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        if (!OptionalBean.ofNullable(groupVO.getPid()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_PID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }

        Group group = new Group();
        group.setPid(groupVO.getPid());
        group.setUserId(userId);
        group.setId(groupVO.getId());
        group.setUpdateTime(new Date());
        group.setUpdatedBy(userName);
        groupService.updateByPrimaryKeySelective(group);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.MODIFY_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }


    /**
     * 删除分组
     *
     * @param groupVO
     * @return
     */
        @RequestMapping(value = "/delGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "删除分组")
    public BaseResponse delGroup(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        if (!OptionalBean.ofNullable(groupVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        Group group = new Group();
        group.setUserId(userId);
        List<Group> groups = groupService.findByAll(group);

        group.setId(groupVO.getId());
        List<Long> subGroupIds = new ArrayList<>();
        buildGroup(group, subGroupIds, groups);
        subGroupIds.add(groupVO.getId());
        List<MqttSub> mqttSubList = mqttSubService.findListByGid(subGroupIds);
        if (CollectionUtils.isNotEmpty(mqttSubList)) {

            // EMQ 指定客户端主题取消订阅 topic
            String url = emqxMqttProperties.getHostUrl();
            String postUri = Oauth2Constant.EMQ_API_HTTP
                    + url.substring(0, url.lastIndexOf(":")).substring(url.indexOf(":")) + Oauth2Constant.EMQ_API_UN_SUBSCRIBEBATCH_ADR;
            List<EmqApiEntity> entityList = mqttSubList.parallelStream().map(
                    mqttSub -> {
                        EmqApiEntity entity = new EmqApiEntity();
                        entity.setQos(2);
                        entity.setTopic(mqttSub.getTopic());
                        entity.setClientid(mqttSub.getClientid());
                        return entity;
                    }
            ).collect(Collectors.toList());
            emqApiService.subscribeBatch(postUri, entityList);
        }
        mqttSubService.deleteByGroupId(subGroupIds);
        groupDeviceService.batchDelete(subGroupIds);
        groupService.batchDel(subGroupIds);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.DELETE_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }

    /**
     * 递归构建
     *
     * @param subGroupIds
     * @param groups
     */
    private void buildGroup(Group needGroup, List<Long> subGroupIds, List<Group> groups) {
        List<Group> children = groups.stream().filter(group -> (needGroup.getId().equals(group.getPid()))).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(children)) {//有子分类的情况
            subGroupIds.addAll(children.stream().map(Group::getId).collect(Collectors.toList()));
            children.forEach(child -> buildGroup(child, subGroupIds, groups));//再次递归构建
        }
    }


    /**
     * 查询分组
     * 提供了（该组下的所有设备,没用到已注释）
     *
     * @return
     */
    @RequestMapping(value = "/getGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "查询分组以及该组下的设备")
    public BaseResponse getGroup(HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);

        Group group = new Group();
        group.setUserId(userId);

//        GroupDevice groupDevice = new GroupDevice();
//        groupDevice.setTenantId(tenantId);
//        List<GroupDevice> devices = groupDeviceService.selectSelective(groupDevice);
//        Map<Long, List<GroupDevice>> listMap =
//                devices.stream().collect(
//                        Collectors.groupingBy(GroupDevice::getGid));
//        List<GroupDTO> groups = getCategories(group, listMap);
        List<GroupDTO> groups = getCategories(group);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.SELECT_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage(), groups);
    }


    /**
     * 查询指定pid下面的子集列表
     *
     * @return
     */
    @RequestMapping(value = "/getGroupByPid", method = RequestMethod.POST)
    @OperationAnnotation(content = "查询指定pid下面的子集列表")
    public BaseResponse getGroupByPid(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        String userName = MayiktJwtUtils.getUsername(request, secretKey);
        if (!OptionalBean.ofNullable(groupVO.getPid()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_PID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        List<Long> ids = commonService.getUserIds(userId);
        GroupDTO group = new GroupDTO();
        group.setIds(ids);
        // 获取当前组下面所有的子组列表
        List<Group> groups = getNeedGroupByPid(group, groupVO.getPid());
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.SELECT_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage(), groups);
    }


    /**
     * 查询分组下的设备
     *
     * @return
     */
    @RequestMapping(value = "/getDeviceByGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "查询分组下的设备")
    public BaseResponse getDeviceByGroup(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        if (!OptionalBean.ofNullable(groupVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        List<Long> ids = commonService.getUserIds(userId);
        List<InGroupDTO> devices = groupDeviceService.getInGroupDevice(ids, groupVO.getId());
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.SELECT_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage(), devices);
    }


    /**
     * 查询未分组的设备
     *
     * @return
     */
    @RequestMapping(value = "/getOutGroupDevice", method = RequestMethod.POST)
    @OperationAnnotation(content = "查询未分组的设备")
    public BaseResponse getOutGroupDevice(HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        List<Long> ids = commonService.getUserIds(userId);
        List<OutGroupDTO> data = groupDeviceService.getOutGroupDevice(ids);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.SELECT_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage(), data);
    }

    /**
     * 查询组不带树状 + 排除自身子集
     *
     * @return
     */
    @RequestMapping(value = "/getGroupList", method = RequestMethod.POST)
    @OperationAnnotation(content = "查询组不带树状 + 排除自身子集")
    public BaseResponse getGroupList(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        List<Long> ids = commonService.getUserIds(userId);
        // 传递自身的主键id
        if (!OptionalBean.ofNullable(groupVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        GroupDTO group = new GroupDTO();
        group.setIds(ids);
        List<Group> groups = getExcludeGroups(group, groupVO.getId());
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.SELECT_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage(), groups);
    }

    /**
     * 获取 排除自身子集 后的id列表
     *
     * @return
     */
    public List<Group> getExcludeGroups(GroupDTO group, Long groupId) throws Exception {
        List<Group> dealCategories = groupService.queryByAll(group);//从缓存或数据库中查询全部

        // 获取当前组下面所有的子组列表
        List<Group> roots = dealCategories.stream().filter(dealCategory -> (dealCategory.getPid().equals(groupId))).collect(Collectors.toList());
        //对根分类进行排序
        roots.sort(new Comparator<Group>() {
            @Override
            public int compare(Group o1, Group o2) {
                return o1.getId() > o2.getId() ? 1 : -1;
            }
        });

        //递归构建结构化的分类信息
        List<Long> idList = new ArrayList<>();
        idList.addAll(roots.stream().map(Group::getId).collect(Collectors.toList()));
        roots.forEach(root -> getSubsetIds(idList, root, dealCategories));

        List<Group> excludeGroup = dealCategories.stream().filter(
                id -> !idList.contains(id.getId())
        ).collect(Collectors.toList());

        return excludeGroup;
    }


    /**
     * 获取 自身子集 后的id列表
     *
     * @return
     */
    public List<Group> getNeedGroupByPid(Group group, Long groupId) throws Exception {
        List<Group> dealCategories = groupService.findByAll(group);//从缓存或数据库中查询全部

        // 获取当前组下面所有的子组列表
        List<Group> roots = dealCategories.stream().filter(dealCategory -> (dealCategory.getPid().equals(groupId))).collect(Collectors.toList());
//        //对根分类进行排序
//        roots.sort(new Comparator<Group>() {
//            @Override
//            public int compare(Group o1, Group o2) {
//                return o1.getId() > o2.getId() ? 1 : -1;
//            }
//        });
//
//        //递归构建结构化的分类信息
//        List<Long> idList = new ArrayList<>();
//        roots.forEach(root -> getSubsetIds(idList, root, dealCategories));
//
//        List<Group> needGroup = dealCategories.stream().filter(
//                id -> idList.contains(id.getId())
//        ).collect(Collectors.toList());

//        return needGroup;
        return roots;
    }

    /**
     * 获取自身子集的id列表
     *
     * @param idList
     * @param group
     * @param dealCategories
     */
    private void getSubsetIds(List<Long> idList, Group group, List<Group> dealCategories) {
        List<Group> children = dealCategories.stream().filter(sub -> (sub.getPid().equals(group.getId()))).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(children)) {//有子分类的情况
            idList.addAll(children.stream().map(Group::getId).collect(Collectors.toList()));
            children.forEach(child -> getSubsetIds(idList, child, dealCategories));//再次递归构建
        }
    }


    /**
     * 查询所有分类,按顺序显示
     * 显示出树状
     *
     * @return
     */
    public List<GroupDTO> getCategories(Group group) throws Exception {
        List<Group> dealCategories = groupService.findByAll(group);//从缓存或数据库中查询全部
        //JDK8的stream处理,把根分类区分出来
        List<GroupDTO> roots = dealCategories.stream().filter(dealCategory -> (dealCategory.getPid() == 0)).
                map(p -> {
                    GroupDTO node = BabaBeanUtils.voToDto(p, GroupDTO.class);
//                    node.setDetail(listMap.get(p.getId()));
                    return node;
                }).
                collect(Collectors.toList());
        //对根分类进行排序
        roots.sort(new Comparator<GroupDTO>() {
            @Override
            public int compare(GroupDTO o1, GroupDTO o2) {
                return o1.getId() > o2.getId() ? 1 : -1;
            }
        });

        //把非根分类区分出来
        List<GroupDTO> subs = dealCategories.stream().filter(dealCategory -> (dealCategory.getPid() != 0)).map(p -> {
            GroupDTO subNode = BabaBeanUtils.voToDto(p, GroupDTO.class);
//            subNode.setDetail(listMap.get(p.getId()));
            return subNode;
        }).collect(Collectors.toList());

        //递归构建结构化的分类信息
        roots.forEach(root -> buildSubs(root, subs));
        return roots;
    }

    /**
     * 递归构建
     *
     * @param parent
     * @param subs
     */
    private void buildSubs(GroupDTO parent, List<GroupDTO> subs) {
        List<GroupDTO> children = subs.stream().filter(sub -> (sub.getPid().equals(parent.getId()))).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(children)) {//有子分类的情况
            parent.setChildren(children);
            children.forEach(child -> buildSubs(child, subs));//再次递归构建
        }
    }

    /**
     * 设备入组
     *
     * @return
     */
    @RequestMapping(value = "/inGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "设备入组")
    public BaseResponse inGroup(@RequestBody GroupDeviceVO groupDeviceVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getAppSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        String userName = MayiktJwtUtils.getUsername(request, secretKey);
        Long userId = commonService.getUserId(request);
        List<String> imeis = groupDeviceVO.getImeis();
        if (CollectionUtils.isEmpty(imeis)) {
            systemParam = systemParamByCacheService.getValues(DeviceConstant.DEVICE_ENUM, DeviceConstant.DEVICE_NO_IMEI_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        if (!OptionalBean.ofNullable(groupDeviceVO.getGroupId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }

        // 查询已在该组中的设备
        List<String> existGroupList = groupDeviceService.getInGroupDeviceByImeis(imeis, groupDeviceVO.getGroupId());
        if (CollectionUtils.isNotEmpty(existGroupList)) {
            imeis = imeis.stream().filter(
                    imei -> !existGroupList.stream().collect(Collectors.toSet()).contains(imei)
            ).collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(imeis)) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_IMEI_EXIST + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }

//        // 筛选出已经分配了组的设备
//        List<String> exitList = groupDeviceService.getGroupDeviceList(imeis);
//
//        //筛选- 筛选未分组的卡
//        Set<String> optImeis = imeis.stream().filter(
//                imei -> !exitList.stream().collect(Collectors.toSet()).contains(imei)
//        ).collect(Collectors.toSet());
        // 筛选出NB设备
        List<String> exitList = groupDeviceService.getNbDeviceList(imeis);

        //筛选非出NB设备
        Set<String> optImeis = imeis.stream().filter(
                imei -> !exitList.stream().collect(Collectors.toSet()).contains(imei)
        ).collect(Collectors.toSet());

        List<EmqApiEntity> entityList = new ArrayList<>();

        // 插入设备需要新增订阅的主题
        if (CollectionUtils.isNotEmpty(optImeis)) {
            String topic = Oauth2Constant.GROUP_PRE + "/" + userId + "/" + groupDeviceVO.getGroupId();
            List<MqttSub> mqttSubList = optImeis.parallelStream().map(imei -> {
                MqttSub mqttSub = new MqttSub();
                mqttSub.setClientid(Oauth2Constant.CLIENT_PRE + imei);
                mqttSub.setTopic(topic);
                mqttSub.setQos(2);
                mqttSub.setCreateTime(DateUtil.getCurrentDate());

                EmqApiEntity entity = new EmqApiEntity();
                entity.setClientid(Oauth2Constant.CLIENT_PRE + imei);
                entity.setTopic(topic);
                entity.setQos(2);
                entityList.add(entity);
                return mqttSub;
            }).collect(Collectors.toList());
            mqttSubService.batchInsert(mqttSubList);
        }

        if (CollectionUtils.isNotEmpty(entityList)) {
            // EMQ 指定客户端主题批量订阅 topic
            String url = emqxMqttProperties.getHostUrl();
            String postUri = Oauth2Constant.EMQ_API_HTTP
                    + url.substring(0, url.lastIndexOf(":")).substring(url.indexOf(":")) + Oauth2Constant.EMQ_API_SUBSCRIBE_ADR;
            emqApiService.subscribeBatch(postUri, entityList);
        }

        List<GroupDevice> groupDeviceList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(imeis)) {
            // 设备入组
            groupDeviceList = imeis.stream().map(imei -> {
                GroupDevice groupDevice = new GroupDevice();
                groupDevice.setImei(imei);
                groupDevice.setUserId(userId);
                groupDevice.setGid(groupDeviceVO.getGroupId());
                groupDevice.setCreateTime(new Date());
                groupDevice.setCreatedBy(userName);
                return groupDevice;
            }).collect(Collectors.toList());
        }

        if (CollectionUtils.isNotEmpty(groupDeviceList)) {
            groupDeviceService.batchInsert(groupDeviceList);
        }
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.ADD_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }

    /**
     * 设备换组（只支持单个）
     *
     * @return
     */
    @RequestMapping(value = "/changeGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "设备换组（只支持单个）")
    public BaseResponse changeGroup(@RequestBody GroupDeviceVO groupDeviceVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        Long userId = commonService.getUserId(request);
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        String userName = MayiktJwtUtils.getUsername(request, secretKey);
        if (!OptionalBean.ofNullable(groupDeviceVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        if (!OptionalBean.ofNullable(groupDeviceVO.getGroupId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }

        GroupDevice oldGroupDevice = groupDeviceService.selectByPrimaryKey(groupDeviceVO.getId());
        if (oldGroupDevice == null) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        String imei = oldGroupDevice.getImei();

        // 查询设备是否存在换组中
        GroupDevice check = new GroupDevice();
        check.setImei(imei);
        check.setGid(groupDeviceVO.getGroupId());
        List<GroupDevice> groupDevices = groupDeviceService.findByAll(check);
        if (CollectionUtils.isNotEmpty(groupDevices)) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_IMEI_EXIST + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());

        }

        String oldTopic = Oauth2Constant.GROUP_PRE + "/" + userId + "/" + oldGroupDevice.getGid();
        String topic = Oauth2Constant.GROUP_PRE + "/" + userId + "/" + groupDeviceVO.getGroupId();

        // EMQ 指定客户端主题取消订阅 topic
        EmqApiEntity entity = new EmqApiEntity();
        entity.setClientid(Oauth2Constant.CLIENT_PRE + imei);
        entity.setTopic(oldTopic);
        entity.setQos(2);

        String url = emqxMqttProperties.getHostUrl();
        String postUri = Oauth2Constant.EMQ_API_HTTP
                + url.substring(0, url.lastIndexOf(":")).substring(url.indexOf(":")) + Oauth2Constant.EMQ_API_UN_SUBSCRIBE_ADR;
        emqApiService.subscribe(postUri, entity);

        // EMQ 指定客户端主题订阅 topic
        entity.setTopic(topic);
        entity.setQos(2);
        postUri = Oauth2Constant.EMQ_API_HTTP
                + url.substring(0, url.lastIndexOf(":")).substring(url.indexOf(":")) + Oauth2Constant.EMQ_API_SUBSCRIBE_ADR;
        emqApiService.subscribe(postUri, entity);

        // 修改设备需要修改订阅的主题
        String client = Oauth2Constant.CLIENT_PRE + imei;
        mqttSubService.updateTopicByClientIdAndOldTopic(oldTopic, client, topic, DateUtil.getCurrentDate(), userName);

        GroupDevice groupDevice = new GroupDevice();
        groupDevice.setId(groupDeviceVO.getId());
        groupDevice.setGid(groupDeviceVO.getGroupId());
        groupDevice.setUpdatedBy(userName);
        groupDevice.setUpdateTime(new Date());
        groupDeviceService.updateByPrimaryKeySelective(groupDevice);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.MODIFY_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }


    /**
     * 设备出组
     *
     * @return
     */
    @RequestMapping(value = "/outGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "设备出组")
    public BaseResponse outGroup(@RequestBody GroupDeviceVO groupDeviceVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        List<Long> ids = groupDeviceVO.getIds();
        List<MqttSub> mqttSubList = mqttSubService.findListByGroupId(ids);
        if (CollectionUtils.isNotEmpty(mqttSubList)) {
            // EMQ 指定客户端主题取消订阅 topic
            String url = emqxMqttProperties.getHostUrl();
            String postUri = Oauth2Constant.EMQ_API_HTTP
                    + url.substring(0, url.lastIndexOf(":")).substring(url.indexOf(":")) + Oauth2Constant.EMQ_API_UN_SUBSCRIBEBATCH_ADR;
            List<EmqApiEntity> entityList = mqttSubList.parallelStream().map(
                    mqttSub -> {
                        EmqApiEntity entity = new EmqApiEntity();
                        entity.setQos(2);
                        entity.setTopic(mqttSub.getTopic());
                        entity.setClientid(mqttSub.getClientid());
                        return entity;
                    }
            ).collect(Collectors.toList());
            emqApiService.subscribeBatch(postUri, entityList);
            mqttSubService.batchDel(mqttSubList.stream().map(MqttSub::getId).collect(Collectors.toList()));
        }
        groupDeviceService.batchDel(ids);
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.DELETE_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }

    /**
     * 组之间联动指向[支持批量插入，修改，删除]
     *
     * @return
     */
    @RequestMapping(value = "/linkGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "组之间联动指向")
    public BaseResponse linkGroup(@RequestBody GroupLinkVO groupLinkVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        String userName = MayiktJwtUtils.getUsername(request, secretKey);
        Long userId = commonService.getUserId(request);
        if (!OptionalBean.ofNullable(groupLinkVO.getGSource()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_PID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        List<Long> userIds = commonService.getUserIds(userId);
        GroupLinkDTO oldLink = new GroupLinkDTO();
        oldLink.setIds(userIds);
        List<GroupLink> oldGroupLinks = groupLinkService.queryByAll(oldLink);
        List<Long> gTargets = groupLinkVO.getGTarget();
        if (CollectionUtils.isEmpty(gTargets) && CollectionUtils.isNotEmpty(oldGroupLinks)) {
            // 删除
            List<Long> ids = oldGroupLinks.stream().map(GroupLink::getId).collect(Collectors.toList());
            groupLinkService.batchDel(ids);
            systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.DELETE_SUCCESS_PREFIX + language);
            return setResultSuccess(systemParam.getMassage());
        }

        if (CollectionUtils.isEmpty(gTargets)) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        // 筛选不在 gTargets 中的做删除操作
        List<Long> removeIds = oldGroupLinks.stream().filter(
                groupLink -> !gTargets.stream().collect(Collectors.toList()).contains(groupLink.getGTarget())
        ).map(GroupLink::getId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(removeIds)) {
            groupLinkService.batchDel(removeIds);
        }

        // 筛选不在 oldGroupLinks 中的做插入操作
        List<GroupLink> groupLinks = gTargets.stream().filter(
                gTarget -> !oldGroupLinks.stream().map(GroupLink::getGTarget).collect(Collectors.toList()).contains(gTarget)
        ).map(gTarget -> {
            GroupLink groupLink = new GroupLink();
            groupLink.setGSource(groupLinkVO.getGSource());
            groupLink.setUserId(userId);
            groupLink.setGTarget(gTarget);
            groupLink.setCreateTime(new Date());
            groupLink.setCreatedBy(userName);
            return groupLink;
        }).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(groupLinks)) {
            groupLinkService.batchInsert(groupLinks);
        }

        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.ADD_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }


    /**
     * 查询当前组可联动的列表，已联动的组列表选中
     *
     * @return
     */
    @RequestMapping(value = "/getLinkGroup", method = RequestMethod.POST)
    @OperationAnnotation(content = "查询当前组可联动的列表，已联动的组列表选中")
    public BaseResponse getLinkGroup(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        if (!OptionalBean.ofNullable(groupVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        List<Long> userIds = commonService.getUserIds(userId);
        List<LinkGroupDTO> groupLinks = groupLinkService.getLinkGroup(userIds, groupVO.getId());
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.SELECT_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage(), groupLinks);
    }

    /**
     * 查询联动列表（前端保证传递pid为为0的id）
     *
     * @return
     */
    @RequestMapping(value = "/getLinkGroupList", method = RequestMethod.POST)
    @OperationAnnotation(content = "查询联动列表（前端保证传递pid为为0的id）")
    public BaseResponse getLinkGroupList(@RequestBody GroupVO groupVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        Long userId = commonService.getUserId(request);
        if (!OptionalBean.ofNullable(groupVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        List<Long> userIds = commonService.getUserIds(userId);
        GroupLinkDTO groupLink = new GroupLinkDTO();
        groupLink.setIds(userIds);
        List<GroupLink> groupLinks = groupLinkService.queryByAll(groupLink);

        List<GroupLinkDTO> groupLinkDTOS = groupLinks.stream().map(gl -> {
            GroupLinkDTO node = BabaBeanUtils.voToDto(gl, GroupLinkDTO.class);
            return node;
        }).collect(Collectors.toList());

        GroupDTO group = new GroupDTO();
        group.setIds(userIds);
        List<Group> groups = groupService.queryByAll(group);
        List<Group> needGroup = groups.stream().filter(g -> (g.getId().equals(groupVO.getId()))).collect(Collectors.toList());
        GroupLinkDTO parent = new GroupLinkDTO();
        parent.setGSource(groupVO.getId());
        if (CollectionUtils.isNotEmpty(needGroup)) {
            parent.setGroupName(needGroup.get(0).getGroupName());
        }

        buildGroupLink(parent, groupLinkDTOS, groups);
        groupLink.setGSource(groupVO.getId());
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.ADD_SUCCESS_PREFIX + language);

        return setResultSuccess(systemParam.getMassage(), parent);
    }


    /**
     * 递归构建
     *
     * @param parent
     * @param groupLinkDTOS
     */
    private void buildGroupLink(GroupLinkDTO parent, List<GroupLinkDTO> groupLinkDTOS, List<Group> groups) {
        List<GroupLinkDTO> firstChildren = groupLinkDTOS.stream().filter(sub -> (sub.getGSource().equals(parent.getGSource()))).map(gl -> {
            groups.stream().forEach(
                    group -> {
                        if (group.getId().equals(gl.getGTarget())) {
                            gl.setGroupName(group.getGroupName());
                        }
                    }
            );
            return gl;
        }).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(firstChildren)) {//有子分类的情况
            parent.setChildren(firstChildren);
            firstChildren.forEach(child -> buildGroupLink(child, groupLinkDTOS, groups));//再次递归构建
        }
    }


    /**
     * 删除组之间联动指向
     *
     * @return
     */
    @RequestMapping(value = "/removeLink", method = RequestMethod.POST)
    @OperationAnnotation(content = "删除组之间联动指向")
    public BaseResponse removeLink(@RequestBody GroupLinkVO groupLinkVO, HttpServletRequest request) throws Exception {
        SystemParam systemParam = null;
        String secretKey = commonService.getWebSalt(request);
        String language = MayiktJwtUtils.getLanguage(request, secretKey);
        if (!OptionalBean.ofNullable(groupLinkVO.getId()).isPresent()) {
            systemParam = systemParamByCacheService.getValues(GroupConstant.GROUP_ENUM, GroupConstant.GROUP_NO_ID_PREFIX + language);
            throw new DefinitException(systemParam.getCode(), systemParam.getMassage());
        }
        groupLinkService.deleteByPrimaryKey(groupLinkVO.getId());
        systemParam = systemParamByCacheService.getValues(CrudConstant.CRUD_ENUM, CrudConstant.DELETE_SUCCESS_PREFIX + language);
        return setResultSuccess(systemParam.getMassage());
    }


}
